package org.acooke.uykfd.db

import java.sql.Connection

import org.apache.empire.db.DBJoinType
import org.apache.empire.db.DBQuery
import org.apache.empire.db.DBReader

import scala.collection.JavaConversions._
import scala.util.Random


trait LfmArtistTagRow
  extends WeightedEdgeRow[CanonicalRow] {

  def tagCanonical(cnxn: Connection): CanonicalRow
  def artistCanonical(cnxn: Connection): CanonicalRow
}

trait LfmArtistTagTable
  extends WeightedEdgeTable[
    LfmArtistTagRow, CanonicalRow, Schema.Canonicals, LfmArtistTagTable, Schema.LfmArtistTags] {

  def raiseWeight(artist: CanonicalRow, weight: Int, tag: CanonicalRow, cnxn: Connection):
      LfmArtistTagRow
  def fromArtistCanonical(artistCanonical: ArtistCanonicalRow, cnxn: Connection): List[LfmArtistTagRow]
  def untaggedArtists(cnxn: Connection, maxCount: Int): List[CanonicalRow]
  def randomTaggedArtist(cnxn: Connection): ArtistRow
  def show(name: String, cnxn: Connection)
  def showTagged(cnxn: Connection)
  def showUntagged(cnxn: Connection)
}

private class LfmArtistTagFactory
  extends WeightedEdgeFactory[
    CanonicalRow, Schema.Canonicals, LfmArtistTagRecord, LfmArtistTagTable, Schema.LfmArtistTags](
    Schema.LFM_ARTIST_TAGS, Schema.CANONICALS, Schema.CANONICALS.orm.fromId)
  with LfmArtistTagTable {

  def rowFactory = new LfmArtistTagRecord(nodeFactory)
  val random = new Random

  def raiseWeight(artist: CanonicalRow, weight: Int, tag: CanonicalRow, cnxn: Connection) =
    fromNodesWithWeight(artist, weight, tag, cnxn)

  def fromArtistCanonical(artistCanonical: ArtistCanonicalRow, cnxn: Connection) =
    fromSourceId(artistCanonical.canonicalId, cnxn)

  /**
   * Canonical values related to artists which have no tags.
   */
  def untaggedArtists(cnxn: Connection, maxCount: Int) = {
    val cmd = Schema.createCommand
    cmd.select(Schema.ARTIST_CANONICAL.CANONICAL)
    cmd.selectDistinct()
    cmd.join(Schema.ARTIST_CANONICAL.CANONICAL, table.SOURCE, DBJoinType.LEFT)
    cmd.where(table.SOURCE.is(null))
    val reader = new DBReader()
    // this failed to work with a list comprehension - no idea why
    var rows = List[CanonicalRow]()
    try {
      reader.open(cmd, cnxn)
      for (row <- reader.iterator(maxCount)) {
        rows ::= Schema.CANONICALS.orm.fromId(Record.asInt(row, Schema.ARTIST_CANONICAL.CANONICAL), cnxn)
      }
    } finally {
      reader.close
    }
    rows
  }

  def randomTaggedArtist(cnxn: Connection): ArtistRow = {
    val n = Schema.ARTISTS.orm.maxArtistId(cnxn)
    def read: ArtistRow =
      try {
        val cmd = Schema.createCommand
        cmd.join(Schema.ARTISTS.ID, Schema.ARTIST_CANONICAL.ARTIST, DBJoinType.LEFT)
        cmd.join(Schema.ARTIST_CANONICAL.CANONICAL, table.SOURCE, DBJoinType.LEFT)
        cmd.where(table.SOURCE.isNot(null))
        cmd.where(Schema.ARTISTS.ID.is(1 + random.nextInt(n)))
        cmd.select(Schema.ARTISTS.ID)
        Schema.ARTISTS.orm.fromId(Schema.querySingleInt(cmd.getSelect, 0, cnxn), cnxn)
      } catch {
        case e: Exception => read
      }
    read
  }

  def show(name: String, cnxn: Connection) {
    Schema.ARTISTS.orm.lookupValue(name, cnxn) match {
      case Some(artist) =>
        for (canonical <- Schema.ARTIST_CANONICAL.orm.fromArtist(artist, cnxn);
             edge <- Schema.LFM_ARTIST_TAGS.orm.fromArtistCanonical(canonical, cnxn))
        yield println("" + edge.tagCanonical(cnxn).value + " " + edge.weight)
      case None => println(name + " not found")
    }
  }

  /**
   * Artists that have no tages, then the canonical values.
   */
  def showUntagged(cnxn: Connection) {
    val tags = Schema.createCommand
    tags.join(Schema.ARTIST_CANONICAL.CANONICAL, Schema.LFM_ARTIST_TAGS.SOURCE)
    tags.select(Schema.ARTIST_CANONICAL.ARTIST)
    val qTags = new DBQuery(tags)
    val qTagsArtist = qTags.findQueryColumn(Schema.ARTIST_CANONICAL.ARTIST)
    val cmd = Schema.createCommand
    cmd.join(Schema.ARTISTS.ID, qTagsArtist, DBJoinType.LEFT)
    cmd.where(qTagsArtist.is(null))
    cmd.orderBy(Schema.ARTISTS.VALUE)
    cmd.select(Schema.ARTISTS.ID)
    val reader = new DBReader()
    try {
      reader.open(cmd, cnxn)
      for (row <- reader.iterator()) {
        val artist = Schema.ARTISTS.orm.fromId(Record.asInt(row, Schema.ARTISTS.ID), cnxn)
        println(artist.value)
        for (canonical <- Schema.ARTIST_CANONICAL.orm.fromArtist(artist, cnxn)) {
          println(" " + canonical.canonical(cnxn))
        }
      }
    } finally {
      reader.close
    }
  }

  def showTagged(cnxn: Connection) {
    val tags = Schema.createCommand
    tags.join(Schema.ARTIST_CANONICAL.CANONICAL, Schema.LFM_ARTIST_TAGS.SOURCE)
    tags.select(Schema.ARTIST_CANONICAL.ARTIST)
    val qTags = new DBQuery(tags)
    val qTagsArtist = qTags.findQueryColumn(Schema.ARTIST_CANONICAL.ARTIST)
    val cmd = Schema.createCommand
    cmd.join(Schema.ARTISTS.ID, qTagsArtist, DBJoinType.LEFT)
    cmd.where(qTagsArtist.isNot(null))
    cmd.orderBy(Schema.ARTISTS.VALUE)
    cmd.select(Schema.ARTISTS.ID)
    cmd.selectDistinct()
    val reader = new DBReader()
    try {
      reader.open(cmd, cnxn)
      for (row <- reader.iterator()) {
        val artist = Schema.ARTISTS.orm.fromId(Record.asInt(row, Schema.ARTISTS.ID), cnxn)
        println(artist.value)
      }
    } finally {
      reader.close
    }
  }

}

private class LfmArtistTagRecord(nf: (Int, Connection) => CanonicalRow)
  extends WeightedEdgeRecord[
    CanonicalRow, Schema.Canonicals, LfmArtistTagRecord, LfmArtistTagTable, Schema.LfmArtistTags](
    Schema.LFM_ARTIST_TAGS, nf)
  with LfmArtistTagRow {

  def tagCanonical(cnxn: Connection) = sink(cnxn)
  def artistCanonical(cnxn: Connection) = source(cnxn)
}
